package org.raphaello.Prototype;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import org.raphaello.core.Primitive;
import org.raphaello.core.states.State;
import org.raphaello.core.jobs.Job;

public class RaphaelloView extends SurfaceView implements SurfaceHolder.Callback {
	
	private static final String TAG = "RaphaelloView";
	
	private String mCurrentState;
	
	private SurfaceHolder mSurfaceHolder;
	private Bitmap mOffScreenBitmap;
	private BitmapDrawable mOffScreenBitmapDrawable;
	private Canvas mOffScreenCanvas;
	
	/** The handler for state transition */
	private Handler mHandler;
	
	/** The message identifier of 'what' */
	private static final int MESSAGE_STATE_TRANSITION = 1;
	
	public RaphaelloView(Context context) {
		super(context);
		
		/* Register the surface holder callback */
		getHolder().addCallback(this);
		
		mCurrentState = null;
		mHandler = new Handler() {
			@Override
			public void handleMessage (Message msg) {
				if(msg.what == MESSAGE_STATE_TRANSITION){
					String newState = (String)msg.obj;
					Raphaello.getView().transitTo(newState);
				}
			}
		};
	}
	
	/** The state transition function to be executed when all of pending message are done */
	public void transition (String state) {
		mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_STATE_TRANSITION, state));
	}

	
	/** State transition function */
	private void transitTo (String state) {
		Log.i(TAG, "State transition from " + mCurrentState + " to " + state);
		
		mCurrentState = state;
		
		for(State s : Raphaello.mStates.values()){
			if(s.getId().equals(state)){
				for(Primitive p : s.getChildren()){
					Job j = (Job)p;
					j.work();
				}
			}
		}
		
		/* Flip the screen when the target state is *.painting */
		if(mCurrentState.endsWith(".painting")){
			flip();
		}
	}
	
	
	/** A touch event must be transformed to a state */
	@Override
	public boolean onTouchEvent (MotionEvent event) {
		Log.d(TAG, "TouchEvent x=" + event.getX() + ", y=" + event.getY());
		
		/* Do picking and transit to *.pressed state */
		
		return false;
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		Log.d(TAG, "surfaceChanged");
		
		mSurfaceHolder = holder;
		createOffScreenBuffer(format, width, height);
		
		/* The initial state transition */
		if(mCurrentState == null)
			transition("org.raphaello.simple.init");
		
		/* Must repaint all of visible objects */
		transition("org.raphaello.simple.color-rect.painting");
	}

	public void surfaceCreated(SurfaceHolder holder) {
		/* Resume the state transition */
		Log.d(TAG, "surfaceCreated");
		
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		/* Pause the state transition */
		Log.d(TAG, "surfaceDestroyed");
	}
	
	/** 
	 * Create the off-screen buffer according to the passed parameters. 
	 * This function must be called before filp() function is called.
	 */
	private void createOffScreenBuffer(int format, int width, int height) {
		Log.d(TAG, "Creating off-buffer=" + format + ", " + width + ", " + height);
		
		mOffScreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
		mOffScreenBitmapDrawable = new BitmapDrawable(mOffScreenBitmap);
		mOffScreenBitmapDrawable.setBounds(0, 0, width, height);
		mOffScreenCanvas = new Canvas(mOffScreenBitmap);
	}
	
	public Canvas getOffScreenCanvas() {
		if(mOffScreenCanvas == null){
			Log.e(TAG, "getOffScreenCanvas must be called after the off-screen buffer is created");
		}
		return mOffScreenCanvas;
	}
	
	/**
	 * This function must be called after the surfaceChanged is invoked. If not, do nothing.
	 */
	private void flip() {
		Log.d(TAG, "flip is called");
		
		if(mSurfaceHolder == null){
			Log.e(TAG, "The surface holder is not created.");
			return;
		}
		
		Canvas c = null;
		try {
			c = mSurfaceHolder.lockCanvas(null);
			mOffScreenBitmapDrawable.draw(c);
		}
		finally {
			if(c != null)
				mSurfaceHolder.unlockCanvasAndPost(c);
		}
	}
}
